#include "Palindrome.h"

/*
 *--------------------------------------------------------------------------------------
 *       Class:  PalindromeUtil
 *      Method:  PalindromeUtil
 * Description:  Contrói uma instância passando o modo
 *--------------------------------------------------------------------------------------
 */
PalindromeUtil::PalindromeUtil(bool printPrime)
{
    this->printPrime = printPrime;
    palins = new Palindromes();
    nPrimes = 0;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  PalindromeUtil
 *      Method:  ~PalindromeUtil
 * Description:  Destrói a instância
 *--------------------------------------------------------------------------------------
 */
PalindromeUtil::~PalindromeUtil()
{
    /* Deleta os palíndromes encontrados */
    for(uint i = 0; i < palins->size(); i++)
        delete palins->at(i);
    delete palins;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  PalindromeUtil
 *      Method:  checkForPalindromes
 * Description:  Procura por palíndromos em uma sequência de palavras ou frases
 *--------------------------------------------------------------------------------------
 */
Palindromes * PalindromeUtil::checkForPalindromes(const Tokens *tokens)
{
    Palindromes *p = new Palindromes();

    for(uint i = 0; i < tokens->size(); i++)
    {
        string *s = (*tokens)[i];
        if(isPalindrome(*s)) /* Se a palavra ou frase for palíndroma */
        {
            Palindrome *palin = new Palindrome();

            strcpy(palin->palindrome, s->c_str());
            p->push_back(palin);
            palins->push_back(palin);
        }
    }

    return p;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  PalindromeUtil
 *      Method:  isPalindrome
 * Description:  Verifice se a string passada é um palíndromo
 *--------------------------------------------------------------------------------------
 */
bool PalindromeUtil::isPalindrome(const string& s)
{
    int size = s.length();

    /* Faz uma iteração até o meio da palavra */
    for(int i = 0, j = size - 1; i < j; i++, j--)
    {
        /* Pula os caracteres que não são alfanuméricos em i */
        for(; i < size && !isalnum(s[i]); i++)
            ;

        /* Pula os caracteres que não são alfanuméricos em j */
        for(; j >= 0 && !isalnum(s[j]); j--)
            ;

        if(i >= size || j < 0 || i > j)
            return false;

        /* Verifica o complemento de cada caracter */
        if(tolower(s[i]) != tolower(s[j]))
            return false;
    }

    return true;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Parser
 *      Method:  operator<<
 * Description:  Imprime todos os palíndromos
 *--------------------------------------------------------------------------------------
 */
ostream& operator<<(ostream& os, PalindromeUtil &putil)
{
    for(uint i = 0; i < putil.palins->size(); i++)
    {
        Palindrome *palin = putil.palins->at(i);
        os << palin->palindrome;
        if(putil.printPrime)
        {
            if(palin->isPrime)
                os << "\t\tO - PRIME";
            else
                os << "\t\tX";
        }
        os << endl;
    }
    return os;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Parser
 *      Method:  getSize
 * Description:  Retorna quantidade de palíndromos
 *--------------------------------------------------------------------------------------
 */
int PalindromeUtil::getSize()
{
    return palins->size();
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Parser
 *      Method:  incNPrimes
 * Description:  Incrementa a quantidade de números primos
 *--------------------------------------------------------------------------------------
 */
void PalindromeUtil::incNPrimes(uint sum)
{
    nPrimes += sum;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Parser
 *      Method:  getNPrimes
 * Description:  Retorna quantidade de números primos encontrados
 *--------------------------------------------------------------------------------------
 */
int PalindromeUtil::getNPrimes()
{
    return nPrimes;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Parser
 *      Method:  sendTo
 * Description:  Envia os palíndromos para um destino
 *--------------------------------------------------------------------------------------
 */
void PalindromeUtil::sendTo(int dest)
{
    int size = palins->size();

    /* Envia a quantidade de palíndromos */
    MPI_Send(&size, 1, MPI_INT, dest, 1, MPI_COMM_WORLD);
    /* Envia todos os palíndromos */
    for(int i = 0; i < size; i++)
        MPI_Send(palins->at(i), sizeof(Palindrome), MPI_BYTE, dest, 1, MPI_COMM_WORLD);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Parser
 *      Method:  receiveFrom
 * Description:  Recebe os palíndromos de uma fonta
 *--------------------------------------------------------------------------------------
 */
void PalindromeUtil::receiveFrom(int src)
{
    int size;
    MPI_Status status;

    /* Recebe a quantidade de palíndromos a ser recebida */
    MPI_Recv(&size, 1, MPI_INT, src, 1, MPI_COMM_WORLD, &status);
    /* Recebe todos os palíndromos */
    for(int i = 0; i < size; i++)
    {
        Palindrome *palin = new Palindrome;
        MPI_Recv(palin, sizeof(Palindrome), MPI_BYTE, src, 1, MPI_COMM_WORLD, &status);

        palins->push_back(palin);
        if(palin->isPrime)
            nPrimes++;
    }
}
